#include <iostream>
#include <memory>

class Fruit
{
    public:
        virtual void name() = 0;//纯虚函数
};

class Apple : public Fruit
{
    public:
        void name() override
        {
            std::cout << "我是一个苹果!\n";
        }
};

class Banana : public Fruit
{
    public:
        void name() override
        {
            std::cout << "我是一个香蕉!\n";
        }
};


//1.【简单工厂模式】:违背开闭原则--可扩展，但不修改原来的
// class FruitFactory
// {
//     public:
//         static std::shared_ptr<Fruit> create(const std::string& name)
//         {
//             if(name == "苹果")
//             {
//                 return std::make_shared<Apple>();
//             }
//             else
//             {
//                 return std::make_shared<Banana>();
//             }
//         }
// };

//2.【工厂方法模式】
//水果接口
//水果类：苹果，香蕉类
//工厂接口
//苹果工厂，香蕉工厂
// class FruitFactory
// {
//     public:
//         virtual std::shared_ptr<Fruit> create() = 0;//提供一个纯虚函数的接口
// };

//解决了开闭原则，但是
//对应的工厂生产对应的水果，但是如果产品很多，我们就要创建很多工厂

// class AppleFactory : public FruitFactory
// {
//     public:
//         std::shared_ptr<Fruit> create() override
//         {
//             return std::make_shared<Apple>();
//         }
// };

// class BananaFactory : public FruitFactory
// {
//     std::shared_ptr<Fruit> create() override
//     {
//         return std::make_shared<Banana>();
//     }
// };


//3.抽象工厂模式
//大工厂->小工厂，对应的小工厂生产对应不同的产品，还有一个工厂生产类
class Animal
{
    public:
        virtual void name() = 0;
};

class Lamp : public Animal
{
    public:
        void name() override
        {
            std::cout << "我是一只山羊!!!\n";
        }
};

class Dog : public Animal
{
    public:
        void name() override
        {
            std::cout << "我是一只小狗!!!\n";
        }
};

class Factory
{
    public:
        virtual std::shared_ptr<Fruit> getFruit(const std::string& name) = 0;
        virtual std::shared_ptr<Animal> getAnimal(const std::string& name) = 0;
};

class FruitFactory : public Factory
{
    public:
        std::shared_ptr<Animal> getAnimal(const std::string& name)
        {
            return std::shared_ptr<Animal>();
        }
        std::shared_ptr<Fruit> getFruit(const std::string& name)
        {
            if(name == "苹果")
            {
                return std::make_shared<Apple>();
            }
            else
            {
                return std::make_shared<Banana>();
            }
        }
};

class AnimalFactory : public Factory
{
    public:
        std::shared_ptr<Fruit> getFruit(const std::string& name)
        {
            return std::shared_ptr<Fruit>();
        }
        std::shared_ptr<Animal> getAnimal(const std::string& name)
        {
            if(name == "山羊")
            {
                return std::make_shared<Lamp>();
            }
            else
            {
                return std::make_shared<Dog>();
            }
            
        }
       
};

class FactoryProducer
{
    public:
        static std::shared_ptr<Factory> create(const std::string& name)
        {
            if(name == "水果")
            {
                return std::make_shared<FruitFactory>();
            }
            else
            {
                return std::make_shared<AnimalFactory>();
            }
        }
};
int main()
{
    //1.简单工厂模式测试
    // std::shared_ptr<Fruit> fruit = FruitFactory::create("苹果");
    // fruit->name();
    // fruit = FruitFactory::create("香蕉");
    // fruit->name();

    //2.工厂方法模式测试
    // std::shared_ptr<FruitFactory> ff(new AppleFactory());//创建苹果工厂
    // std::shared_ptr<Fruit> fruit = ff->create();//苹果工厂生产苹果对象
    // fruit->name();

    // ff.reset(new BananaFactory());
    // fruit = ff->create();
    // fruit->name();

    //3.抽象工厂模式
    std::shared_ptr<Factory> ff = FactoryProducer::create("水果");//先创建一个水果工厂
    std::shared_ptr<Fruit> fruit = ff->getFruit("苹果");
    fruit->name();
    fruit = ff->getFruit("香蕉");
    fruit->name();

    std::shared_ptr<Factory> factory = FactoryProducer::create("动物");//先创建一个动物工厂
    std::shared_ptr<Animal> animal = factory->getAnimal("小狗");
    animal->name();
    animal = factory->getAnimal("山羊");
    if(animal.get() == nullptr)
    {
        return 0;
    }
    animal->name();


    return 0;
}